package com.wisdom.system.service.impl.sys;

import com.github.pagehelper.Page;
import com.wisdom.system.dao.dev.DevTableMapper;
import com.wisdom.system.dao.sys.SysTableBakJobMapper;
import com.wisdom.system.entity.base.PageRequest;
import com.wisdom.system.entity.base.PageResult;
import com.wisdom.system.entity.dev.DevTableColumn;
import com.wisdom.system.entity.sys.SysTableBakJob;
import com.wisdom.system.common.annotation.ValidationUnique;
import com.wisdom.system.common.exception.LogicException;
import com.wisdom.system.common.mybatis.PageUtil;
import com.wisdom.system.common.mybatis.SqlUtil;
import com.wisdom.system.common.util.ObjectUtil;
import com.wisdom.system.common.util.StringUtil;
import com.wisdom.system.service.sys.SysTableBakJobService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Service
public class SysTableBakJobServiceImpl implements SysTableBakJobService {

    @Autowired
    private SysTableBakJobMapper sysTableBakJobMapper;

    @Autowired
    private DevTableMapper devTableMapper;

    @Value("${spring.datasource.url}")
    private String currentDataBase;

    @Override
    @ValidationUnique(key = {"code", "tableName"}, msg = "（编码或备份表）已存在")
    @Transactional
    public int addSysTableBakJobInfo(SysTableBakJob sysTableBakJob) {
        sysTableBakJob.setTableSchema(getCurrentDataBaseMethod());
        sysTableBakJob.setIsExecuted(0);
        if (sysTableBakJob.getIsClearTableData() == 1 && sysTableBakJob.getIsClearBakData() == 1) {
            throw LogicException.of("不允许同时开启删除源表数据数据、清空备份数据（导致数据全部丢失）");
        }
        int count = sysTableBakJobMapper.insert(sysTableBakJob);
        String tableName = sysTableBakJob.getTableName();
        String tableBakName = tableName + "_bak";
        sysTableBakJobMapper.dropTableBak(tableBakName);
        sysTableBakJobMapper.createTableBak(tableName, tableBakName);
        return count;
    }

    @Override
    @ValidationUnique(key = {"code", "tableName"}, msg = "（编码或备份表）已存在", excludeSelf = true)
    @Transactional
    public int updateSysTableBakJobInfo(SysTableBakJob sysTableBakJob) {
        if (sysTableBakJob.getIsExecuted() == 1) {
            throw LogicException.of("该备份任务已执行过禁止修改");
        }
        if (sysTableBakJob.getIsClearTableData() == 1 && sysTableBakJob.getIsClearBakData() == 1) {
            throw LogicException.of("不允许同时开启删除源表数据数据、清空备份数据（导致数据全部丢失）");
        }
        return sysTableBakJobMapper.updateByPrimaryKey(sysTableBakJob);
    }

    @Override
    public int delSysTableBakJobInfo(String id) {
        return sysTableBakJobMapper.deleteByPrimaryKey(id);
    }

    @Override
    public int delSysTableBakJobList(String ids) {
        String[] idArray = ids.split(",");
        Example example = new Example(SysTableBakJob.class);
        example.createCriteria().andIn("id", Arrays.asList(idArray));
        return sysTableBakJobMapper.deleteByExample(example);
    }

    @Override
    public SysTableBakJob getSysTableBakJobInfo(String id) {
        return sysTableBakJobMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<SysTableBakJob> getSysTableBakJobList(Integer state, String keyword) {
        return getSysTableBakJobExample(state, keyword);
    }

    @Override
    public PageResult<SysTableBakJob> getSysTableBakJobPageList(Integer state, String keyword, PageRequest pageRequest) {
        Page<?> page = PageUtil.startPageAllowNull(pageRequest.getPageNum(), pageRequest.getPageSize());
        return new PageResult<>(getSysTableBakJobExample(state, keyword), page.getTotal());
    }

    private List<SysTableBakJob> getSysTableBakJobExample(Integer state, String keyword) {
        Example example = new Example(SysTableBakJob.class);
        Example.Criteria criteria = example.createCriteria();
        if (state != null) {
            criteria.andEqualTo("state", state);
        }
        if (StringUtil.isNotEmpty(keyword)) {
            criteria.andLike("name", SqlUtil.likeEscapeH(keyword));
        }
        return sysTableBakJobMapper.selectByExample(example);
    }

    @Override
    public List<SysTableBakJob> getSysTableBakJobExecuteList() {
        return sysTableBakJobMapper.getSysTableBakJobExecuteList();
    }

    /**
     * 获取数据库名称列表
     *
     * @return
     */
    @Override
    public List<Map> showDatabase() {
        return sysTableBakJobMapper.showDatabase();
    }

    /**
     * 获取表列表
     *
     * @return
     */
    @Override
    public List<Map> getTableList() {
        String tableSchema = getCurrentDataBaseMethod();
        List<Map> maps = sysTableBakJobMapper.getTableList(tableSchema);
        return maps;
    }

    /**
     * 执行备份操作
     *
     * @param sysTableBakJob
     * @return
     */
    @Override
    @Transactional
    public int executeTableBak(SysTableBakJob sysTableBakJob) {
        int rows;
        if (ObjectUtil.isNull(sysTableBakJob)) {
            throw LogicException.of("传进来的备份数据为空");
        }
        String schema = getCurrentDataBaseMethod();
        if (ObjectUtil.isNull(schema)) {
            throw LogicException.of("表空间不存在");
        }
        String tableName = sysTableBakJob.getTableName();
        String conditions = sysTableBakJob.getConditions();
        String tableBakName = tableName + "_bak";
        //获取两个表的字段
        List<DevTableColumn> fromTableColumns = devTableMapper.getDevTableColumns(schema, tableName);
        List<DevTableColumn> toTableColumns = devTableMapper.getDevTableColumns(schema, tableBakName);
        //校验表是否存在 如果fromTableColumns和toTableColumns为空，则表不存在
        if (CollectionUtils.isEmpty(fromTableColumns) || CollectionUtils.isEmpty(toTableColumns)) {
            throw LogicException.of("fromTableColumns" + "或" + toTableColumns + "表视图不错在");
        }

        if (fromTableColumns.size() != toTableColumns.size()) {
            throw LogicException.of("表字段不对应");
        }
        //当删除源表数据数据且清空备份数据：不允许存在（导致数据全部丢失）。
        if (sysTableBakJob.getIsClearTableData() == 1 && sysTableBakJob.getIsClearBakData() == 1) {
            throw LogicException.of("不允许同时开启删除源表数据数据、清空备份数据（导致数据全部丢失）");
        }
        //（当删除源表数据且不清空备份表：预防状态更新先删除重复数据在插入）
        if (sysTableBakJob.getIsClearTableData() == 1 && sysTableBakJob.getIsClearBakData() == 0) {
            sysTableBakJobMapper.clearRepeatTableBakData(schema, tableName, tableBakName, conditions);
        }
        //当不删除源表数据数据且不清空备份数据：删除备份表相同数据，预防主键冲突。
        if (sysTableBakJob.getIsClearTableData() == 0 && sysTableBakJob.getIsClearBakData() == 0) {
            sysTableBakJobMapper.clearRepeatTableBakData(schema, tableName, tableBakName, conditions);
        }
        //当不删除源表数据数据且清空备份数据：清空数据库。
        if (sysTableBakJob.getIsClearTableData() == 0 && sysTableBakJob.getIsClearBakData() == 1) {
            clearTableData(schema, tableBakName, null);
        }
        rows = sysTableBakJobMapper.insertTableBakList(schema, tableName, schema, tableName + "_bak", conditions);
        // 清除源表查询数据
        if (sysTableBakJob.getIsClearTableData() == 1) {
            int t = clearTableData(schema, tableName, conditions);
            // 判断是否变化的条数一致
            if (rows != t) {
                throw LogicException.of("数据变化不统一，请检查条件是否正确");
            }
        }
        if (sysTableBakJob.getIsExecuted() == 0) {
            sysTableBakJob.setIsExecuted(1);
            sysTableBakJobMapper.updateByPrimaryKey(sysTableBakJob);
        }
        return rows;
    }

    @Override
    @Transactional
    public int updateSysTableBakJobState(String id, int state) {
        return sysTableBakJobMapper.updateSysTableBakJobState(id, state);
    }

    /**
     * 清除表数据
     *
     * @param tableSchema
     * @param tableName
     * @param conditions  查询条件，条件为空时清空所有表
     * @return
     */
    private int clearTableData(String tableSchema, String tableName, String conditions) {
        return sysTableBakJobMapper.clearTableData(tableSchema, tableName, conditions);
    }

    /**
     * 获取当前连接数据库
     *
     * @return
     */
    public String getCurrentDataBaseMethod() {
        int start = currentDataBase.lastIndexOf('/');
        int end = currentDataBase.indexOf('?');
        int len = currentDataBase.length();
        if (end != -1 && start != -1 && start < (len - 1) && end < len) {
            return currentDataBase.substring(start + 1, end);
        }
        return null;
    }
}
